# 类
# 定义一个人类
# 属性：id、name、age
# 方法
# 面向对象三大特性：封装、继承、多态
# 封装：1、代码的封装（类中可以定义属性、方法）
#      2、安全性上的封装（例如Java中的private等访问权限修饰符）
class Person:
    id = '001'

    # name = 'zs'
    # age = 20
    # 构造方法
    # 类中的方法第一个参数都是self 相当于java中的this
    def __init__(self, id, name, age):
        print(self.id)
        # 定义类的属性
        # 定义即赋值
        self.id = id
        self.name = name
        self.age = age
        # 属性名以__开头 可以实现“封装”
        # 但此“封装”并非真正意思上的“封装”
        # 还是可以通过 _类名+属性名 获取该属性
        self.__secret = 'this is a secret'

    # 定义成员方法
    # 调用方法时 不需要传入self参数
    def eat(self, food):
        print("%s can eat %s" % (self.name, food))

    # 当对类的对象调用print方法时 会调用此方法
    # 类似Java的toString方法
    def __str__(self):
        return "%s,%s,%d" % (self.id, self.name, self.age)


# 实例化
zs = Person('001', 'zs', 20)
print(zs.id)
print(zs.name)
print(zs.age)
# print(zs.secret)
print(zs.__secret)
print(zs._Person__secret)
zs.eat('bread')


# 继承
# 定义一个Teacher类
# 属性：id name age subject
# 方法：eat teaching
# 子类继承父类 会自动拥有父类的属性及方法
# 当然在子类中也可以有自己的属性及方法，也能重写父类方法
class Teacher(Person):
    def __init__(self, id, name, age, subject):
        # 使用继承可以减少重复的代码
        super(Teacher, self).__init__(id, name, age)
        # self.id = id
        # self.name = name
        # self.age = age
        self.subject = subject

    # 方法的重写
    def eat(self, food):
        print("Teacher %s can eat %s" % (self.name, food))

    def teaching(self):
        print("Teacher %s can teach students %s" % (self.name, self.subject))


# 实例化
tea_liu = Teacher('10001', 'liu', 27, 'math')
tea_li = Teacher('10002', 'li', 26, 'Chinese')
print(tea_li.name)
print(tea_liu.name)
tea_li.teaching()
tea_liu.teaching()

# 会调用父类中实现的__str__方法
print(tea_li)
print(tea_liu)


# 多态
# 父类引用指向子类对象
# food = 'beer'
# food = 123

# Python 在定义变量及函数的参数是可以限定类型
# 主要用于PyCharm的提示 并不会用于运行时的代码检查
def eatTwice(person: Person):
    food = 'rice'
    person.eat(food)
    person.eat(food)


eatTwice(zs)
# 多态
eatTwice(tea_li)
eatTwice(tea_liu)
